package component;

import org.junit.Assert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import annotation.*;
import annotation.source.IStaticProxy;
import tools.AnnotationUtil;
import tools.PageUtil;

import java.time.Duration;
import java.util.Optional;
import java.util.function.BiPredicate;

public class PageHolder implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public <T> T getPage(T t){
        if (t instanceof IStaticProxy){
            T staticProxy = ((IStaticProxy<T>) t).getStaticProxy();
            if (staticProxy!=null){
                return staticProxy;
            }
        }

        Class<?> tClass = AopUtils.getTargetClass(t);
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(tClass);
        enhancer.setCallback(new PageProxy<T>(t,applicationContext));

        return (T) enhancer.create();
    }

    public <E extends IProxyable<E>,T extends IProxyable<T>> E getPageEntity(T page, E entity){
        Class<?> targetClass = AopUtils.getTargetClass(entity);
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(new PageEntityProxy<T,E>(page,entity,applicationContext));

        return (E) enhancer.create();
    }

    public <T> boolean isInPage(T t){
        Class<?> tClass = AopUtils.getTargetClass(t);
        WebDriver driver = applicationContext.getBean(WebDriver.class);

        Optional<Page> pageOptional = AnnotationUtil.getAnnotation(tClass,Page.class);
        Assert.assertTrue("只支持使用tools.annotation.Page注解的类", pageOptional.isPresent());
        Page page = pageOptional.get();

        Optional<Args> addtionalArgs = AnnotationUtil.getAnnotation(tClass, Args.class);

        return PageUtil.findElement(driver,asBy(page.id()),Duration.ofMillis(page.timeout()))
                .map(element -> {
                    try {
                        BiPredicate<WebElement,Args> predicate = page.predication().newInstance();
                        return predicate.test(element,addtionalArgs.orElse(null));
                    } catch (InstantiationException | IllegalAccessException e) {
                        e.printStackTrace();
                        return false;
                    }
                }).orElse(false);
    }


    public static By asBy(Locator locator){
        return locator.type().asBy(locator.location());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


}
